#include "./Cargo_Manager.h"

Cargo_Manager::Cargo_Manager(PaintSystem * paint_system)
    :paint_system(paint_system)
{
    setupManager();
}

Cargo_Manager::~Cargo_Manager(){
    for(auto &p:shape_define_map){
        auto &fd = p.second;
        if(fd) delete fd;
        fd = null;
    }
    shape_define_map.clear();
    for(auto &p:color_define_map){
        auto &fd = p.second;
        if(fd) delete fd;
        fd = null;
    }
    color_define_map.clear();
}

void Cargo_Manager::setupManager(){
    ShapeDefine *shapedefine = null;
    shapedefine = new ShapeDefine(ShapeType::CIRCLE);
    shape_define_map[shapedefine->serialization()] = shapedefine;
    shapedefine = new ShapeDefine(ShapeType::RECTANGLE);
    shape_define_map[shapedefine->serialization()] = shapedefine;
    shapedefine = new ShapeDefine(ShapeType::STAR);
    shape_define_map[shapedefine->serialization()] = shapedefine;
    shapedefine = new ShapeDefine(ShapeType::FAN);
    shape_define_map[shapedefine->serialization()] = shapedefine;

    for(char c = 64; c <= 71; ++c){
        color_define_map[c] = new ColorDefine((ColorType)c);
    }
}

ShapeCargo *Cargo_Manager::generateShapeCargoWithHashId(std::string hash_id){
    ShapeCargo * shape_cargo = null;
    if (shape_define_map.count(hash_id)){
        shape_cargo = new ShapeCargo(shape_define_map[hash_id]);
    }
    else{
        shape_cargo = new ShapeCargo(shape_define_map[hash_id] = new ShapeDefine(hash_id));
    }

    return shape_cargo;
}

ShapeCargo *Cargo_Manager::generateShapeCargoWithShapeDefine(ShapeDefine *define)
{
    if(define->isNone()){
        delete define;
        return null;
    }
    std::string hash_id = define->serialization();
    if (shape_define_map.count(hash_id))
    {
        ShapeDefine *spd = shape_define_map[hash_id];
        if(spd != define){
            delete define;
            define = shape_define_map[hash_id];
        }
    }
    else
    {
        shape_define_map[hash_id] = define;
    }
    return new ShapeCargo(define);
}

ColorCargo *Cargo_Manager::generateColorCargoWithHashId(char hash_id){
    return new ColorCargo(color_define_map[hash_id]);
}

void Cargo_Manager::paintShapeCargo(int x, int y,gate::GateDirection g,  ShapeCargo * shape){
    paint_system->drawShapeCargo(x,y,g,shape);
}

void Cargo_Manager::paintColorCargo(int x, int y,gate::GateDirection g, ColorCargo *color){
    paint_system->drawColorCargo(x,y,g,color->define->hash_id[0],color);
}

std::pair<ShapeCargo *, ShapeCargo*> Cargo_Manager::cropShape(ShapeCargo *shape){
    std::pair<ShapeDefine *, ShapeDefine*>pdefine = ((ShapeDefine*)shape->define)->beCropped();
    return {
        generateShapeCargoWithShapeDefine(pdefine.first),
        generateShapeCargoWithShapeDefine(pdefine.second)
    };
}

ShapeCargo *Cargo_Manager::colorShapeWithColor(ShapeCargo *shape, ColorCargo *color)
{
    ShapeCargo * newcargo = generateShapeCargoWithShapeDefine(
        ((ShapeDefine *)shape->define)->beColored(((ColorDefine *)color->define)->getcolor()));
    return newcargo;
}

ColorCargo *Cargo_Manager::mixColors(ColorCargo *color1,ColorCargo *color2){
    // mix the color
    ((ColorDefine*)color1->define)->color |= ((ColorDefine*)color2->define)->color;
    ColorCargo* newcargo = color1;
    return newcargo;
}

Cargo *Cargo_Manager::turnDirection(Cargo* cargo,bool is_clockwise){
    if(cargo->define->cargo_type == CargoType::COLOR){
        return cargo;
    }
    // else
    ShapeDefine * newdefine = ((ShapeDefine *)cargo->define)->turnDirection(is_clockwise);

    return generateShapeCargoWithShapeDefine(newdefine);
}

ShapeCargo *Cargo_Manager::stack(ShapeCargo * shape1, ShapeCargo *shape2){
    ShapeDefine *newdefine = ((ShapeDefine*)shape1->define)->stackOther((*(ShapeDefine *)shape2->define));

    return generateShapeCargoWithShapeDefine(newdefine);
}
